home *** CD-ROM | disk | FTP | other *** search
/ HPAVC / HPAVC CD-ROM.iso / PCGPEV10.ZIP / SPEAKER.TXT < prev    next >
Text File  |  1994-05-10  |  12KB  |  303 lines

  1.                      ┌────────────────────────────┐
  2.                      │ Programming the PC Speaker │
  3.                      └────────────────────────────┘
  4.  
  5.             Written for the PC-GPE by Mark Feldman
  6.             e-mail address : u914097@student.canberra.edu.au
  7.                              myndale@cairo.anu.edu.au
  8.  
  9.               ┌───────────────────────────────────────────┐
  10.               │      THIS FILE MAY NOT BE DISTRIBUTED     │
  11.               │ SEPARATE TO THE ENTIRE PC-GPE COLLECTION. │
  12.               └───────────────────────────────────────────┘
  13.  
  14.  
  15. ┌────────────┬───────────────────────────────────────────────────────────────
  16. │ Disclaimer │
  17. └────────────┘
  18.  
  19. I assume no responsibility whatsoever for any effect that this file, the
  20. information contained therein or the use thereof has on you, your sanity,
  21. computer, spouse, children, pets or anything else related to you or your
  22. existance. No warranty is provided nor implied with this information.
  23.  
  24. ┌────────────────────────┬───────────────────────────────────────────────────
  25. │ Basic Programming Info │
  26. └────────────────────────┘
  27.  
  28. The PC speaker has two states, in and out (0 and 1, on and off, Adam and
  29. Eve etc). You can directly set the state of the PC speaker or you can hook
  30. the speaker up to the output of PIT timer 2 to get various effects.
  31.  
  32. Port 61h controls how the speaker will operate as follows:
  33.  
  34. Bit 0    Effect
  35. ─────────────────────────────────────────────────────────────────
  36.   0      The state of the speaker will follow bit 1 of port 61h
  37.   1      The speaker will be connected to PIT channel 2, bit 1 is
  38.          used as switch ie 0 = not connected, 1 = connected.
  39.  
  40. Playing around with the bits in port 61h can prevent the Borland BC++ and
  41. Pascal sound() procedures from working properly. When you are done using the
  42. speaker make sure you set bit's 0 and 1 of port 61h to 0.
  43.  
  44. ┌─────────────────┬──────────────────────────────────────────────────────────
  45. │ Your First Tone │
  46. └─────────────────┘
  47.  
  48. Ok, so lets generate a simple tone. We'll send a string of 0's and 1's to the
  49. PC speaker to generate a square wave. Here's the Pascal routine:
  50.  
  51.  
  52. Uses Crt;
  53.  
  54. const SPEAKER_PORT = $61;
  55.  
  56. var portval : byte;
  57.  
  58. begin
  59.  
  60.   portval := Port[SPEAKER_PORT] and $FC;
  61.  
  62.   while not KeyPressed do
  63.     begin
  64.       Port[SPEAKER_PORT] := portval or 2;
  65.       Delay(5);
  66.       Port[SPEAKER_PORT] := portval;
  67.       Delay(5);
  68.     end;
  69.   ReadKey;
  70. end.
  71.  
  72. On my 486SX33 this generates a tone of around about 100Hz.
  73.  
  74. First this routine grabs the value from the speaker port, sets the lower two
  75. bits to 0 and stores it. The loop first sets the speaker to "on", waits a
  76. short while, sets it to "off" and waits another short while. I write the loop
  77. to do it in this order so that when a key is pressed and the program exits
  78. the loop the lower two bits in the speaker port will both be 0 so it won't
  79. prevent other programs which then use the speaker from working properly.
  80.  
  81. This is a really bad way of generating a tone. While the program is running
  82. interrupts are continually occurring in the PC and this prevents the timing
  83. from being accurate. Try running the program and moving the mouse around.
  84. You can get a nicer tone by disabling interrupts first, but this would
  85. prevent the KeyPressed function from working. In any case we want to
  86. generate a nice tone of a given frequency, and using the Delay procedure
  87. doesn't really allow us to do this. To top it all off, this procedure uses
  88. all of the CPU's time so we can't do anything in the background while the
  89. tone is playing.
  90.  
  91.  
  92. ┌─────────────────────┬──────────────────────────────────────────────────────
  93. │ Using PIT Channel 2 │
  94. └─────────────────────┘
  95.  
  96. Connecting the PC speaker to PIT channel 2 is simply a matter of programming
  97. the channel to generate a square wave of a given frequency and then setting
  98. the lower two bits in the speaker port to a 1. Detailed information on
  99. programming the PIT chip can be found in the file PIT.TXT, but here is
  100. the pascal source you'll need to do the job:
  101.  
  102. const SPEAKER_PORT = $61;
  103.        PIT_CONTROL = $43;
  104.      PIT_CHANNEL_2 = $42;
  105.           PIT_FREQ = $1234DD;
  106.  
  107. procedure Sound(frequency : word);
  108. var counter : word;
  109. begin
  110.  
  111.   { Program the PIT chip }
  112.   counter := PIT_FREQ div frequency;
  113.   Port[PIT_CONTROL] := $B6;
  114.   Port[PIT_CHANNEL_2] := Lo(counter);
  115.   Port[PIT_CHANNEL_2] := Hi(counter);
  116.  
  117.   { Connect the speaker to the PIT }
  118.   Port[SPEAKER_PORT] := Port[SPEAKER_PORT] or 3;
  119. end;
  120.  
  121. procedure NoSound;
  122. begin
  123.   Port[SPEAKER_PORT] := Port[SPEAKER_PORT] and $FC;
  124. end;
  125.  
  126.  
  127. ┌────────────────────────────────────────────┬───────────────────────────────
  128. │ Playing 8-bit Sound Through the PC Speaker │
  129. └────────────────────────────────────────────┘
  130.  
  131. Terminolgy
  132. ──────────
  133.  
  134. To clear up any confusion, here's my own definition of some words I'll be
  135. using in this section:
  136.  
  137. sample : A single value in the range 0-255 representing the input level of
  138.          the microphone at any given moment.
  139.  
  140. volume : A sort of generic version of sample, not limited to the 0-255 range.
  141.  
  142.   song : A bunch of samples in a row representing a continuous sound.
  143.  
  144. string : A bunch of binary values (0-1) in a row.
  145.  
  146.  
  147.  
  148. Programs like the legendary "Magic Mushroom" demo do a handly little trick
  149. to play 8-bit sound from the PC speaker by sending binary strings to the
  150. PC speaker for every sample they play. If the bits are all 0's, then the
  151. speaker will be "off". If they are all 1's the speaker will be "on". If they
  152. alternate 0's and 1's then the speaker will behave as if it's "half" on, and
  153. so forth.
  154.  
  155.                 ┌───────────────────────────────────┐
  156.                 │ Bit string     Time speaker is on │
  157.                 ├───────────────────────────────────┤
  158.                 │ 11111111              100%        │
  159.                 │ 11101110               75%        │
  160.                 │ 10101010               50%        │
  161.                 │ 10001000               25%        │
  162.                 │ 00000000                0%        │
  163.                 └───────────────────────────────────┘
  164.  
  165. Note that in this table I've used strings which are 8 bits long meaning that
  166. there can only be 9 discrete volume levels (since anywhere from 0 to 8 of
  167. them can be set to 1). In reality the strings would be longer.
  168.  
  169. The problem with using bit strings such as this is getting accurate timing
  170. between each bit you send. One way around this is to put all the 1's at the
  171. front of the string and all the 0's at the end, like so:
  172.  
  173.                 ┌───────────────────────────────────┐
  174.                 │ Bit string     Time speaker is on │
  175.                 ├───────────────────────────────────┤
  176.                 │ 11111111              100%        │
  177.                 │ 11111100               75%        │
  178.                 │ 11110000               50%        │
  179.                 │ 11000000               25%        │
  180.                 │ 00000000                0%        │
  181.                 └───────────────────────────────────┘
  182.  
  183. This way you can send all the 1's as a single pulse and your timing doesn't
  184. have to be quite as accurate. The sound isn't quite as good, but I've found
  185. it to be pretty reasonable. A real advantage in using this method is that
  186. you can program the PIT chip for "interrupt on terminal count" mode, this
  187. mode is similar to the one-shot mode, but counting starts as soon as you
  188. load the PIT counter. So if you are playing an 11kHz song you simply load the
  189. PIT counter 11000 times a second with a value that's proportional to the
  190. sample value and trigger it. The speaker output will go low for the set time
  191. and then remain high until the next time you trigger it (in practise it
  192. doesn't matter whether the string of 1's make the speaker go "low" or
  193. "high", just so long as it's consistent). I've managed to get good results
  194. using PIT channel 2 to handle the one-shot for each sample and PIT channel 0
  195. to handle when to trigger channel 2 (ie 11000 times a second). *PLUS* I was
  196. able to have a program drawing stuff on the screen while all this was going
  197. on in the background!
  198.  
  199. Incidently I should mention here that the "interrupt on terminal count" mode
  200. does not generate an actual interrupt on the Intel CPU. The mode was given
  201. this name since the PIT can can be hooked up to a CPU to generate an
  202. interrupt. As far as I can tell IBM didn't do it like this.
  203.  
  204. This technique does have one nasty side-effect though. If you are playing an
  205. 11kHz tone for example then the PC speaker will be being turned on and off
  206. exactly 11000 times a second, in other words you'll hear a nice 11kHz sine
  207. wave superimposed over the song (do any of you math weirdo's want to do
  208. a FFT to prove this for me?). A way around this is to play the song back
  209. at 22kHz and play each sample twice. This will result in a 22kHz sine wave
  210. which will pretty much be filtered out by the tiny PC speaker and the simple
  211. low-pass filter circuit that it's usually connected to on the motherboard.
  212.  
  213. The PIT chip runs at a frequency of 1193181 Hz (1234DDh). If you are playing
  214. an 11kHz song at 22kHz then 1193181 / 22000 = 54 clocks per second, so
  215. you'll have to program the PIT to count a maximum of 54 clocks for each
  216. sample. What I'm getting at is that you'll only be able to play 54 discreet
  217. sample levels using this method, so you'll have to scale the 256 different
  218. levels in an 8-bit song to fit into this range which will also result in
  219. futher loss of sound quality. I sped things up considerably by pre-
  220. calculating a lookup table like so:
  221.  
  222. var count_values : array[0..255] of byte;
  223.  
  224. for level := 0 to 255 do
  225.   count_values[level] := level * 54 div 255;
  226.  
  227. Then for each sample I just look up what it's counter value is and send
  228. that to the PIT chip. Since each value is of byte size you can program the
  229. PIT chip to accept the LSB only (see PIT.TXT for more info). The following
  230. pascal code will set the PIT chip up for "interrupt on terminal count" mode
  231. where only the LSB of the count needs to be loaded:
  232.  
  233. Port[PIT_CONTROL] := $90;
  234. Port[SPEAKER_PORT] := Port[SPEAKER_PORT] or 3;
  235.  
  236. And the following line will trigger the one-shot for a given sample value
  237. from 0-255:
  238.  
  239. Port[PIT_CHANNEL_2] := count_values[sample_value];
  240.  
  241. Do that 22000 times a second and whaddaya know, you'll hear "8-bit" sound
  242. from your PC speaker! Here's a bit of code which works ok on my machine:
  243.  
  244.  
  245. ────────────────────────────────────────────────────────────────────────────
  246.  
  247. const SPEAKER_PORT = $61;
  248.        PIT_CONTROL = $43;
  249.      PIT_CHANNEL_2 = $42;
  250.           PIT_FREQ = $1234DD;
  251.  
  252.      DELAY_LENGTH = 100;
  253.  
  254. procedure PlaySound(sound : PChar; length : word);
  255. var count_values : array[0..255] of byte;
  256.     i, loop : word;
  257. begin
  258.  
  259.   { Set up the count table }
  260.   for i := 0 to 255 do
  261.     count_values[i] := i * 54 div 255;
  262.  
  263.   { Set up the PIT and connect the speaker to it }
  264.   Port[PIT_CONTROL] := $90;
  265.   Port[SPEAKER_PORT] := Port[SPEAKER_PORT] or 3;
  266.  
  267.   { Play the sound }
  268.  
  269.   asm cli end;
  270.   for i := 0 to length - 1 do
  271.     begin
  272.       Port[PIT_CHANNEL_2] := count_values[byte(sound^)];
  273.       for loop := 0 to DELAY_LENGTH do;
  274.       Port[PIT_CHANNEL_2] := count_values[byte(sound^)];
  275.       for loop := 0 to DELAY_LENGTH do;
  276.       sound := sound + 1;
  277.     end;
  278.   asm sti end;
  279.  
  280.   { Reprogram the speaker for normal operation }
  281.   Port[SPEAKER_PORT] := Port[SPEAKER_PORT] and $FC;
  282.   Port[PIT_CONTROL] := $B6;
  283. end;
  284.  
  285. ────────────────────────────────────────────────────────────────────────────
  286.  
  287.  
  288. Note that in this simple example I used a loop of DELAY_LENGTH to get the
  289. timing between samples. I had to fiddle around to get the right value for my
  290. machine and it varies from machine to machine. I also disable interrupts
  291. while the inner loop is playing, otherwise you hear the 18.2Hz timer tick
  292. while the sound was playing.
  293.  
  294.  
  295. Both of these techniques suffer from two drawbacks. The first is that
  296. samples played from the speaker do not sound very loud. You can make them
  297. louder by making the song you are playing louder, but this eventually means
  298. the sample values will start falling outside the 0-255 range and you'll have
  299. to clip them which starts distorting the sound. The second problem is that
  300. this technique doesn't work on the psezio-electric "speakers" inside lap-top
  301. computers.
  302.  
  303.